package com.huan.sensitive.dfa;

import cn.hutool.core.lang.Assert;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.StopChar;
import cn.hutool.dfa.WordTree;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * hutool dfs 敏感词过滤 <a href="https://www.hutool.cn/docs/#/dfa/DFA%E6%9F%A5%E6%89%BE">dfa敏感词过滤</a>
 *
 * @author huan.fu
 * @date 2024/1/21 - 10:44
 */
@RestController
@Slf4j
public class HutoolDfaController {

    private static final WordTree TREE = new WordTree();

    @PostConstruct
    public void initSensitiveWord() {
        // 过滤一些特殊的字符
        TREE.setCharFilter(StopChar::isNotStopChar);

        TREE.addWord("大");
        TREE.addWord("大土豆");
        TREE.addWord("土豆");
        TREE.addWord("刚出锅");
        TREE.addWord("出锅");
        log.info("完成 hutool 敏感词初始化");
    }

    /**
     * 标准匹配，匹配到最短关键词，并跳过已经匹配的关键词
     */
    @GetMapping("matchAll1")
    public List<String> matchAll1() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 匹配到【大】，就不再继续匹配了，因此【大土豆】不匹配
        // 匹配到【刚出锅】，就跳过这三个字了，因此【出锅】不匹配（由于刚首先被匹配，因此长的被匹配，最短匹配只针对第一个字相同选最短）
        List<String> matchAll = TREE.matchAll(text, -1, false, false);
        assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅]");
        return matchAll;
    }

    /**
     * 匹配到最短关键词，不跳过已经匹配的关键词
     */
    @GetMapping("matchAll2")
    public List<String> matchAll2() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 【大】被匹配，最短匹配原则【大土豆】被跳过，【土豆继续被匹配】
        // 【刚出锅】被匹配，由于不跳过已经匹配的词，【出锅】被匹配
        List<String> matchAll = TREE.matchAll(text, -1, true, false);
        assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅, 出锅]");
        return matchAll;
    }

    /**
     * 匹配到最长关键词，跳过已经匹配的关键词 (文档提供的例子，这个有问题)
     */
    @GetMapping("matchAll3")
    public List<String> matchAll3() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 匹配到【大】，由于到最长匹配，因此【大土豆】接着被匹配
        // 由于【大土豆】被匹配，【土豆】被跳过，由于【刚出锅】被匹配，【出锅】被跳过
        List<String> matchAll = TREE.matchAll(text, -1, false, true);
        System.out.println(matchAll);
        assertEquals(matchAll.toString(), "[大, 大土豆, 刚出锅]");
        return matchAll;
    }

    /**
     * 匹配到最长关键词，不跳过已经匹配的关键词（最全关键词）
     */
    @GetMapping("matchAll4")
    public List<String> matchAll4() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 匹配到【大】，由于到最长匹配，因此【大土豆】接着被匹配，由于不跳过已经匹配的关键词，土豆继续被匹配
        // 【刚出锅】被匹配，由于不跳过已经匹配的词，【出锅】被匹配
        List<String> matchAll = TREE.matchAll(text, -1, true, true);
        assertEquals(matchAll.toString(), "[大, 大土豆, 土豆, 刚出锅, 出锅]");
        return matchAll;
    }

    /**
     * 找出所有匹配的关键字
     */
    @GetMapping("matchAllWords")
    public List<FoundWord> matchAllWords() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 找出所有匹配的关键字
        return TREE.matchAllWords(text);
    }

    /**
     * 找出所有匹配的关键字，限制匹配到的个数
     */
    @GetMapping("matchAllWords/{limit}")
    public List<FoundWord> matchAllWords(@PathVariable int limit) {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 找出所有匹配的关键字
        return TREE.matchAllWords(text, limit);
    }

    /**
     * 只会查找第一个匹配的结果，这样一旦找到第一个关键字，就会停止继续匹配
     */
    @GetMapping("match")
    public String match() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 只会查找第一个匹配的结果，这样一旦找到第一个关键字，就会停止继续匹配
        String firstMatchWord = TREE.match(text);
        log.info("第一个匹配到的词是:[{}]", firstMatchWord);
        return firstMatchWord;
    }

    /**
     * 只会查找第一个匹配的结果，这样一旦找到第一个关键字，就会停止继续匹配
     */
    @GetMapping("isMatch")
    public boolean isMatch() {
        // 正文
        String text = "我有一颗大土豆，刚出锅的";
        // 只会查找第一个匹配的结果，这样一旦找到第一个关键字，就会停止继续匹配
        return TREE.isMatch(text);
    }


    private void assertEquals(String obj1, String obj2) {
        if (!obj1.equals(obj2)) {
            throw new IllegalArgumentException("匹配失败");
        }
    }
}
